package com.andova.glcamera.texture

import android.graphics.Bitmap
import android.opengl.GLES30
import com.andova.glcamera.DEFAULT_COLOR_FORMAT
import com.andova.image.GLES
import java.nio.Buffer
import java.nio.ByteBuffer
import java.nio.ByteOrder

internal class Drawer {
    companion object {
        const val FLOAT_BYTE = 4

        /**
         * 切记顶点顺时针或者逆时针，不要缠绕画Z字等
         * 由于顶点坐标系和纹理坐标系的差异，所以，纹理坐标从(0,1)开始
         */
        val BUFFER_PREVIEW_DATA = floatArrayOf(
            -1f, -1f, 0f, 1f,
            1f, -1f, 1f, 1f,
            1f, 1f, 1f, 0f,
            -1f, 1f, 0f, 0f
        )
        val BUFFER_VERTEX_DATA = floatArrayOf(
            -1f, -1f, 0f,
            1f, -1f, 0f,
            1f, 1f, 0f,
            -1f, 1f, 0f
        )
        const val STRIDE_POSITION = 4 * FLOAT_BYTE
        const val STRIDE_TEXTURE = 2 * FLOAT_BYTE
    }

    private val boBuffers = IntArray(4)

    fun bind(width: Int, height: Int, format: Int, x: Float = 1f, y: Float = 1f) {
        unbind()
        pixelBuffer = ByteBuffer.allocate(
            width * height * when (format) {
                GLES30.GL_RGB8, GLES30.GL_RGB -> 3
                GLES30.GL_RGB565 -> 2
                GLES30.GL_RGBA -> 4
                else -> 4
            }
        )
        GLES30.glGenBuffers(boBuffers.size, boBuffers, 0)

        val vertexBufferObjectData = floatArrayOf(
            -x, -y, 0f, 0f,
            x, -y, 1f, 0f,
            x, y, 1f, 1f,
            -x, y, 0f, 1f
        )

        val bytes = vertexBufferObjectData.size * FLOAT_BYTE
        val buffer = ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertexBufferObjectData).position(0)

        // VBO
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, boBuffers[0])
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, vertexBufferObjectData.size * FLOAT_BYTE, buffer, GLES30.GL_STATIC_DRAW)
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_NONE)

        // PBO 1
        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, boBuffers[1])
        GLES30.glBufferData(GLES30.GL_PIXEL_PACK_BUFFER, width * height * Int.SIZE_BYTES, null, GLES30.GL_STREAM_READ)
        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, GLES30.GL_NONE)

        // PBO 2
        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, boBuffers[2])
        GLES30.glBufferData(GLES30.GL_PIXEL_PACK_BUFFER, width * height * Int.SIZE_BYTES, null, GLES30.GL_STREAM_READ)
        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, GLES30.GL_NONE)

        val vertexBuffer = ByteBuffer.allocateDirect(BUFFER_VERTEX_DATA.size * FLOAT_BYTE).order(ByteOrder.nativeOrder()).asFloatBuffer().put(BUFFER_VERTEX_DATA).position(0)
        // VBO
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, boBuffers[3])
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, BUFFER_VERTEX_DATA.size * FLOAT_BYTE, vertexBuffer, GLES30.GL_STATIC_DRAW)
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_NONE)
    }

    fun unbind() {
        pixelBuffer?.clear()
        pixelBuffer = null
        GLES30.glDeleteBuffers(boBuffers.size, boBuffers, 0)
    }

    fun drawFrameByNormal(vertexLoc: Int, textureLoc: Int) {
        val vertexData = floatArrayOf(
            -1f, -1f,
            1f, -1f,
            1f, 1f,
            -1f, 1f
        )
        val vertexBuffer = ByteBuffer.allocateDirect(vertexData.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertexData).position(0)

        val texPosData = floatArrayOf(
            0f, 0f,
            1f, 0f,
            1f, 1f,
            0f, 1f
        )
        val texPosBuffer = ByteBuffer.allocateDirect(texPosData.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(texPosData).position(0)

        GLES30.glEnableVertexAttribArray(vertexLoc)
        GLES30.glVertexAttribPointer(vertexLoc, 2, GLES30.GL_FLOAT, false, 0, vertexBuffer)
        GLES30.glEnableVertexAttribArray(textureLoc)
        GLES30.glVertexAttribPointer(textureLoc, 2, GLES30.GL_FLOAT, false, 0, texPosBuffer)
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, 4)
        GLES30.glDisableVertexAttribArray(vertexLoc)
        GLES30.glDisableVertexAttribArray(textureLoc)
    }

    fun drawFrameByVBO(vertexLoc: Int, textureLoc: Int) {
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, boBuffers[0])
        GLES30.glEnableVertexAttribArray(vertexLoc)
        GLES30.glEnableVertexAttribArray(textureLoc)

        GLES30.glVertexAttribPointer(vertexLoc, 2, GLES30.GL_FLOAT, false, STRIDE_POSITION, 0)
        GLES30.glVertexAttribPointer(textureLoc, 2, GLES30.GL_FLOAT, false, STRIDE_POSITION, STRIDE_TEXTURE)
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, 4)

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_NONE)
        GLES30.glDisableVertexAttribArray(vertexLoc)
        GLES30.glDisableVertexAttribArray(textureLoc)
    }

    fun drawFrameVertex(vertexLoc: Int) {
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, boBuffers[3])
        GLES30.glEnableVertexAttribArray(vertexLoc)

        GLES30.glVertexAttribPointer(vertexLoc, 3, GLES30.GL_FLOAT, false, 0, 0)
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, 4)

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_NONE)
        GLES30.glDisableVertexAttribArray(vertexLoc)
    }

    private var pbo1Index = 1
    private var pbo2Index = 2

    @Volatile
    var pixelBuffer: ByteBuffer? = null

    /**
     * glReadPixels 数组是像素的大小 而不是像素分量的大小 故用 IntBuffer
     */
    fun readPixels(width: Int, height: Int, format: Int = DEFAULT_COLOR_FORMAT) {
        GLES30.glReadPixels(
            0, 0, width, height, format,
            when (format) {
                GLES30.GL_RGB, GLES30.GL_RGBA -> GLES30.GL_UNSIGNED_BYTE
                GLES30.GL_RGB565 -> GLES30.GL_UNSIGNED_SHORT_5_6_5
                else -> GLES30.GL_UNSIGNED_BYTE
            },
            pixelBuffer
        )
    }

    /**
     * 单单只是这样的话，glReadPixels和glMapBufferRange耗时确实低了，但预览画面还是很卡
     */
    fun readFrame(width: Int, height: Int, format: Int = DEFAULT_COLOR_FORMAT) {
        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, boBuffers[pbo1Index])
        GLES.glReadPixels(width, height)
//        GLES30.glReadPixels(0, 0, width, height, format,
//                when (format) {
//                    GLES30.GL_RGB, GLES30.GL_RGBA -> GLES30.GL_UNSIGNED_BYTE
//                    GLES30.GL_RGB565 -> GLES30.GL_UNSIGNED_SHORT_5_6_5
//                    else -> GLES30.GL_UNSIGNED_BYTE
//                }, null)
        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, boBuffers[pbo2Index])
        val buffer = GLES30.glMapBufferRange(
            GLES30.GL_PIXEL_PACK_BUFFER, 0,
            width * height * when (format) {
                GLES30.GL_RGB8, GLES30.GL_RGB -> 3
                GLES30.GL_RGB565 -> 2
                GLES30.GL_RGBA -> 4
                else -> 4
            }, GLES30.GL_MAP_READ_BIT
        )
        GLES30.glUnmapBuffer(GLES30.GL_PIXEL_PACK_BUFFER)
        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, GLES30.GL_NONE)
        pbo1Index = pbo1Index % 2 + 1
        pbo2Index = pbo2Index % 2 + 1
    }

    fun drawAndReadYuvPixels(parameter: Parameter, format: Int = DEFAULT_COLOR_FORMAT) {
        val tex = IntArray(1)
        GLES30.glGenTextures(tex.size, tex, 0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, tex[0])
        GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, format, parameter.surfaceWidth, parameter.surfaceHeight, 0, format, GLES30.GL_UNSIGNED_BYTE, null)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)

        val fbo = IntArray(1)
        GLES30.glGenFramebuffers(fbo.size, fbo, 0)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fbo[0])
        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, tex[0], 0)

        drawFrameVertex(1)

        GLES30.glReadPixels(0, 0, parameter.surfaceWidth, parameter.surfaceHeight, format, GLES30.GL_UNSIGNED_BYTE, pixelBuffer)

        GLES30.glDeleteFramebuffers(fbo.size, fbo, 0)
        GLES30.glDeleteTextures(tex.size, tex, 0)
    }

    @Deprecated("just for test")
    private fun testConvertToBitmap(width: Int, height: Int, buffer: Buffer) {
        buffer.rewind()
        val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        bitmap.copyPixelsFromBuffer(buffer)
        bitmap?.recycle()
    }
}